/*
A tool similar rsh
Client send shell command to mutiple servers and then print the result on stdout
This code is for client
Author: Zhuo Chen (Calvin)
*/ 

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<errno.h>
#include<sys/types.h>
#include<netdb.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<pthread.h>
#include<semaphore.h>
#include<sys/stat.h>
#include<fcntl.h>

#define MAXSLEEP 128 /*the max sleep time,about 120 seconds*/
#define BUFLEN 128
#define RESULTBUF 10240
#define SERVER_PORT 1501
#define THREADNUM 10
#define TEMPNAMELEN 10

/*parament for the thread*/
struct thread_pam_t{
	int num;
	char * hostname;
	char * command;
	FILE * fp;
};
sem_t sem[THREADNUM];
pthread_t thread[THREADNUM];
struct thread_pam_t thread_pam[THREADNUM];


int connect_try(int sockfd, const struct sockaddr *addr, socklen_t alen);
void get_result(int sockfd,FILE *fp);
int send_command(int sockfd,char * command);
void * do_client(void *argv);

void *
do_client(void *argv){
	struct sockaddr_in dest_addr;
	struct hostent *host;
	unsigned long inaddr=01;
	int sockfd,err,rc,num,n;
	FILE *fp;
	char *hostname,*command,buf[BUFLEN];
	struct thread_pam_t pam=*((struct thread_pam_t *)argv);

	hostname=pam.hostname;
	command=pam.command;
	num=pam.num;
	fp=pam.fp;

	n=sprintf(buf,"hostname=%s\n",pam.hostname);
	fwrite(buf,sizeof(char),n,fp);
	
	// parse the hostname : whether a hostname or a ip address?
	inaddr = inet_addr(hostname);
	if( inaddr != INADDR_NONE){ // is a ip address
		dest_addr.sin_family = AF_INET;
		dest_addr.sin_addr.s_addr = inaddr;
	} else {
		if( (host=gethostbyname(hostname) )==NULL){ 
			printf("gethostbyname error");
			pthread_exit((void *)-1);
		}
		dest_addr.sin_family=host->h_addrtype;
		memcpy( (char *)&dest_addr.sin_addr,host->h_addr_list[0],host->h_length );
	}
	dest_addr.sin_port=htons(SERVER_PORT);
	
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
		err=errno;
		printf("creat socket error!\n");
	}

	if((rc=connect_try(sockfd, (struct sockaddr*)&dest_addr, sizeof(dest_addr)))<0){
		err=errno;
	} else if(rc==0) {
		if((rc=send_command(sockfd, command))<0){
			sem_post(&sem[num]);
			pthread_exit((void *)-1);
		}
		
		get_result(sockfd,fp);
		close(sockfd);
		sem_post(&sem[num]);
		pthread_exit((void *)1);
	}

	fprintf(stdout,"can't connect to %s:%s\n",hostname,strerror(err));
	sem_post(&sem[num]);
	pthread_exit((void *)-1);
}

/*try to connect the server*/
int 
connect_try(int sockfd, const struct sockaddr * addr, socklen_t alen){
	int nsec;
	for(nsec=1;nsec<MAXSLEEP;nsec<<=1){
		if(connect(sockfd,addr,alen)==0)	
			return 0;
		if(nsec<=MAXSLEEP/2)
			sleep(nsec);
	}
	return -1;
}

/*send the command to the server*/
int
send_command(int sockfd,char *command){

	int err;
	char buf[BUFLEN];
	strcpy(buf,command);
	if((err=send(sockfd,buf,sizeof(buf),0))<0){
		printf("send the parament error!\n");
		return -1;
	}
	return 1;
}

/*get the result and write in the tempfile named by thread_pam[i].num*/
void
get_result(int sockfd,FILE *fp){
	int n;
	char buf[RESULTBUF];
	while((n=recv(sockfd,buf,RESULTBUF,0))>0)
		fwrite(buf,sizeof(char),n,fp);
	if(n<0)
		printf("receive the result from server error\n");
}


int
main(int argc,char *argv[]){
	struct hostent *host;
	struct sockaddr_in local_addr;
	int i, servernum;
	char command[BUFLEN],buf[BUFLEN];
	char tempname[TEMPNAMELEN];
	if(argc<3){
		printf("usage: .\rsh -n hostnumbers hostnames ....  command\n");
		return -1;
	}

	servernum=atoi(argv[2]);
	bzero(command,sizeof(command));
	for(i=3+servernum;i<argc;i++){
		strcat(command,argv[i]);
		strcat(command," ");
	}

	printf("command=%s,servernum=%d\n",command,servernum);
	for(i=0;i<THREADNUM;i++)
		sem_init(&sem[i],0,0);

	local_addr.sin_family=AF_INET;
	local_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	local_addr.sin_port=htons(0);

	/*start the thread parallelly*/
	for(i=0;i<servernum;i++){
		thread_pam[i].num=i;
		thread_pam[i].hostname=argv[i+3];
		thread_pam[i].command=command;
		sprintf(tempname,"%d",i);
		thread_pam[i].fp=fopen(tempname,"wb+");
		if(pthread_create(&thread[i],NULL,do_client,&thread_pam[i])){
			printf("can't creat a thread for %s",thread_pam[i].hostname);
			return -1;
		}
	}

	for(i=0;i<servernum;i++)/*wait all the thread finish*/
		sem_wait(&sem[i]);
	
	/*print the result from the tempfile on stdout sequencially, and then delete the tempfile*/	
	for(i=0;i<servernum;i++){
		fclose(thread_pam[i].fp);
		sprintf(tempname,"%d",thread_pam[i].num);
		thread_pam[i].fp=fopen(tempname,"r");
		if(unlink(tempname)<0)
		printf("unlink %s error\n",thread_pam[i].hostname);
		while(fgets(buf,BUFLEN,thread_pam[i].fp)!=NULL){
			if(fputs(buf,stdout)==EOF)
			printf("puts error\n");
		}
		printf("\n");
		fclose(thread_pam[i].fp);
	}
	return 1;
}




